function [x, w, xnonlin, fesd, fess, Zdemand, Zsupply, Zinside, X_id] = final_variables(df, price_iv, model) 

    % Define supply instruments
    Zsupply = [df.bmcpost df.mcpost df.dist];

    disp(['Model: ', model]);

    % Coarsen income if income not coarsened already
    if median(df.minc)<max(df.minc) && median(df.minc)>min(df.minc) && ~strcmp(model,'og')
        df.minc = coarsen_income(df.minc);
    end

    % Define fixed effects
    datefe = df.dateid == unique(df.dateid)';
    montfe = df.montid == unique(df.montid)';
    prodfe = df.prodid == unique(df.prodid)';
    brndfe = df.brndid == unique(df.brndid)';
    cityfe = df.cityid == unique(df.cityid)';
    fesd   = [prodfe montfe(:, 2:end)];
    fess   = [prodfe cityfe(:, 2:end) montfe(:, 2:end)];
    brndcd = findgroups(df.brndid,df.cdid);
    shelf  = accumarray(brndcd,1);
    df.shelf  = shelf(brndcd);

    % calculate instruments
    Znested = [df.tdist df.nprod df.bmc.*df.tdist df.bmc.*df.nprod df.bud.*df.tdist df.bud.*df.nprod];
    Znonlin = [df.minc df.minc.*df.import df.minc.*df.calor df.minc.*df.sizeid];
    if strcmp(price_iv,'price')
        Zprice  = [df.p_jt];
    else
        Zprice  = [df.bmcpost df.dist];
    end

    % calculate LI instruments for each model
    if strcmp(model,'L') || strcmp(model,'RCL') 
        fesd = [brndfe montfe(:, 2:end) df.minc];
        Zdemand = f_licols([Zprice Znested Znonlin]);
    elseif strcmp(model,'L_nm')
        fesd = [brndfe df.minc];
        Zdemand = f_licols([Zprice Znested Znonlin]);
    elseif strcmp(model,'og')
        fesd   = [prodfe datefe(:, 2:end)];
        fess   = [prodfe cityfe(:, 2:end) datefe(:, 2:end)];
        Zdemand = f_licols([Zprice Znested Znonlin]);
    elseif strcmp(model,'og_sh')
        fesd   = [prodfe datefe(:, 2:end) df.shelf];
        fess   = [prodfe cityfe(:, 2:end) datefe(:, 2:end)];
        Zdemand = f_licols([Zprice Znested Znonlin]);
    else
        Zdemand = f_licols([Zprice Znested Znonlin]);
    end

    % residualize the instruments
    Zinside = Zdemand;
    X_id    = nan(size(Zdemand(:,1)));

    if strcmp(price_iv,'sg') || strcmp(price_iv,'price') || strcmp(price_iv,'inc') || strcmp(price_iv,'prod')
        X_resid  = [df.montid,df.prodid,df.minc];
    elseif strcmp(price_iv,'coarse')
        X_resid  = [df.prodid];
    end

    if strcmp(price_iv,'sg') || strcmp(price_iv,'coarse')
        for i = 1:length(Zinside(1,:))
            disp(['Residualizing Z: column ',int2str(i)]);
            [Zinside(:,i),X_id] = residualizer(Zinside(:,i),X_resid,df.cdid,'nonparametric');
        end
        if strcmp(price_iv,'coarse')
            for i = 1:length(fesd(1,:))
                disp(['Residualizing fesd: column ',int2str(i)]);
                [fesd(:,i),X_id] = residualizer(fesd(:,i),X_resid,df.cdid,'nonparametric');
            end
            nonzerocols = (max(abs(fesd))>0.00001);
            fesd     = fesd(:,nonzerocols);
        end
    elseif strcmp(price_iv,'inc') 
        for i = 1:length(Zinside(1,:))
            disp(['Residualizing Z: column ',int2str(i)]);
            [resid,X_id] = residualizer(Zinside(:,i),X_resid,df.cdid,'nonparametric');
            Zinside(:,i) = Zinside(:,i)-resid;
        end
    elseif strcmp(price_iv,'prod')
        groups = findgroups(array2table(X_resid));
        for i = 1:length(Zinside(1,:))
            temp = accumarray(groups,Zinside(:,i),[],@mean);
            Zinside(:,i) = Zinside(:,i)-temp(groups);
        end
        for i = 1:length(fesd(1,:))
            temp = accumarray(groups,fesd(:,i),[],@mean);
            fesd(:,i) = fesd(:,i)-temp(groups);
        end
        nonzerocols = (max(abs(fesd))>0.00001);
        fesd     = fesd(:,nonzerocols);
    elseif strcmp(price_iv,'price') || strcmp(price_iv,'dist')
        Zinside = Zprice;
    end


    nonzerocols = (max(abs(Zinside))>0.00001);
    Zinside     = Zinside(:,nonzerocols);

    % Variables for demand and supply
    x = [df.p_jt fesd];
    w = [df.mcpost df.dist fess];
    xnonlin = [df.p_jt ones(size(df.p_jt)) df.calor];
end
